package p.ithorns.framework.common.uuid;

import java.net.NetworkInterface;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Enumeration;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * SimpleId
 * <p>
 * 生成24位字符串ID
 *
 * @author liuhaitao
 * @version 1.0
 * @date 2022/3/9 10:37
 */
public class SimpleId implements Comparable<SimpleId> {

    private static final String INC_LOCK = new String("SimpleId.INC_LOCK");

    private static int nextInc = (new java.util.Random()).nextInt();

    private static int genTime = flip((int) (System.currentTimeMillis() / 1000));

    // 创建调度线程池（核心线程数按需配置）
    private static final ScheduledExecutorService TIME_FIXER = Executors.newSingleThreadScheduledExecutor();

    private static final int GEN_MACHINE;
    static {
        try {
            final int machinePiece;
            {
                StringBuilder sb = new StringBuilder();
                Enumeration<NetworkInterface> e = NetworkInterface.getNetworkInterfaces();
                while (e.hasMoreElements()) {
                    NetworkInterface ni = e.nextElement();
                    sb.append(ni.toString());
                }
                machinePiece = sb.toString().hashCode() << 16;
            }

            final int processPiece = java.lang.management.ManagementFactory
                    .getRuntimeMXBean().getName().hashCode() & 0xFFFF;
            GEN_MACHINE = machinePiece | processPiece;
        } catch (java.io.IOException ioe) {
            throw new RuntimeException(ioe);
        }

        // 2. 定义任务
        Runnable task = () -> {
            genTime = flip((int) (System.currentTimeMillis() / 1000));
        };

        // 3. 启动周期性任务（立即执行，之后每100ms固定间隔执行）
        TIME_FIXER.scheduleAtFixedRate(task, 0, 500, TimeUnit.MILLISECONDS);
    }

    /**
     * 调用该方法获取24位字符串ID
     */
    public static String get() {
        return new SimpleId().toString();
    }


    private final int time;
    private final int machine;
    private final int inc;

    public SimpleId() {
        this.time = genTime;
        this.machine = GEN_MACHINE;
        synchronized (INC_LOCK) {
            this.inc = nextInc++;
        }
    }

    @Override
    public int hashCode() {
        return inc;
    }

    public String toHexStr() {
        byte[] b = toByteArray();

        StringBuilder buf = new StringBuilder(24);

        for (byte value : b) {
            int x = value & 0xFF;
            String s = Integer.toHexString(x);
            if (s.length() == 1)
                buf.append("0");
            buf.append(s);
        }

        return buf.toString();
    }

    public byte[] toByteArray() {
        byte[] b = new byte[12];
        ByteBuffer bb = ByteBuffer.wrap(b);
        bb.putInt(inc);
        bb.putInt(machine);
        bb.putInt(time);
        reverse(b);
        return b;
    }

    public String toString() {
        return toHexStr();
    }

    public int compareTo(SimpleId id) {
        if (id == null)
            return -1;

        long xx = id.getTime() - getTime();
        if (xx > 0)
            return -1;
        else if (xx < 0)
            return 1;

        int x = id.machine - machine;
        if (x != 0)
            return -x;

        x = id.inc - inc;
        if (x != 0)
            return -x;

        return 0;
    }


    public long getTime() {
        long z = flip(time);
        return z * 1000;
    }

    public int getInc() {
        return inc;
    }

    public int getMachine() {
        return machine;
    }

    static int flip(int x) {
        byte[] b = new byte[4];
        ByteBuffer bb = ByteBuffer.wrap(b);
        bb.order(ByteOrder.LITTLE_ENDIAN);
        bb.putInt(x);
        bb.flip();
        bb.order(ByteOrder.BIG_ENDIAN);
        return bb.getInt();
    }

    static void reverse(byte[] b) {
        for (int i = 0; i < b.length / 2; i++) {
            byte t = b[i];
            b[i] = b[b.length - (i + 1)];
            b[b.length - (i + 1)] = t;
        }
    }

    static String pos(String s, int p) {
        return s.substring(p * 2, (p * 2) + 2);
    }

}